package com.cmes.system.core.service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cmes.cache.service.CacheService;
import com.cmes.core.utils.Preconditions;
import com.cmes.jdbc.service.GenericService;
import com.cmes.system.core.constant.ErrorCode;
import com.cmes.system.core.entity.ConfigEntity;
import com.cmes.system.core.repository.ConfigRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 配置管理
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public class ConfigService extends GenericService<ConfigRepository, ConfigEntity> {

    private static final String CACHED_KEY = "system:config";
    private final CacheService cacheService;

    @PostConstruct
    public void init() {
        cacheService.register(CACHED_KEY, "全部系统配置");
    }

    /**
     * 提供给外部查询
     *
     * @param code 编码
     * @return 配置值
     */
    public ConfigEntity getByCode(String code) {
        List<ConfigEntity> entities = cacheService.getCache(CACHED_KEY, this::list);
        return entities.stream()
                .filter(v -> Objects.equals(code, v.getCode()))
                .findFirst()
                .orElse(null);
    }

    /**
     * 提供给外部查询
     *
     * @param code 编码
     * @return 配置值
     */
    public String getValueByCode(String code) {
        return Optional.ofNullable(getByCode(code))
                .map(ConfigEntity::getValue)
                .orElse(null);
    }

    /**
     * 提供给外部查询
     *
     * @param code 编码
     * @return 配置值
     */
    public String getValueByCode(String code, String defaultValue) {
        return Optional.ofNullable(getByCode(code))
                .map(ConfigEntity::getValue)
                .orElse(defaultValue);
    }

    /**
     * 提供给外部查询
     *
     * @param codes 编码
     * @return 配置值
     */
    public List<ConfigEntity> getByCode(Collection<String> codes) {
        List<ConfigEntity> entities = cacheService.getCache(CACHED_KEY, this::list);
        return entities.stream()
                .filter(v -> codes.contains(v.getCode()))
                .collect(Collectors.toList());
    }

    // region 页面增删查改操作

    public void canAdd(ConfigEntity entity) {
        checkCode(entity.getCode());
    }

    /**
     * 是否可修改
     * @param id ID
     */
    public void canUpdate(Long id) {
        ConfigEntity db = getById(id);
        checkExist(db);
    }

    public void canDelete(List<Long> ids) {
        List<ConfigEntity> dbs = listByIds(ids);
        checkExist(dbs, ids.size());
        checkSystem(dbs);
    }

    private void checkExist(ConfigEntity db) {
        Preconditions.isNull(db, ErrorCode.CONFIG_NOT_EXISTS);
    }

    private void checkExist(List<ConfigEntity> dbs, int count) {
        Preconditions.isEmpty(dbs, ErrorCode.CONFIG_NOT_EXISTS);
        if (dbs.size() < count) {
            Preconditions.isTrue(true, ErrorCode.CONFIG_NOT_EXISTS);
        }
    }

    private void checkSystem(List<ConfigEntity> dbs) {
        List<String> sysCodes = dbs.stream()
                .filter(v -> Boolean.TRUE.equals(v.getSys()))
                .map(ConfigEntity::getCode)
                .collect(Collectors.toList());
        Preconditions.notEmpty(sysCodes, ErrorCode.CONFIG_IS_SYS, sysCodes, "删除");
    }

    /**
     * 校验部门名称是否唯一
     *
     * @param code code
     */
    private void checkCode(String code) {
        Preconditions.check(() -> exists(Wrappers.<ConfigEntity>lambdaQuery()
                .eq(ConfigEntity::getCode, code)), ErrorCode.CONFIG_CODE_EXISTS);
    }

    // endregion

    // region 缓存操作

    public void removeCache() {
        try {
            cacheService.removeCache(CACHED_KEY);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    // endregion

}
